﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


///Okay, this class is pretty self explanatory, but I'll go ahead and explain for you anyways.
///[Arin's robot voice]: Hello, and welcome to the place for you will learn enemy class. 
///
//Practically the same way you did whatever it is you did, originally, do it again, and I'll faind a way to make it a touch cleaner, unless you already do so.

//MAKE ALL NON ACCESSABLE VARIABLES PRIVATE, DAMMIT

namespace MyImaginaryFriends_Redux.Screens.GamePlayScreen_Classes
{
    enum EnemyType
    {
        Mannequin = 1,
        Knife = 2,

    }

    enum EnemyState
    {
        Idle,       //Enemy is just chillin'.
        Patrol,     //Enemy walks through its route.
        Attack,     //Enemy is in midst of attacking.
        Aggro,      //Enemy spots Mallory and seeks.
        Avoiding,
        Death       //Enemy is defeated.
    }

    enum MoveDirection
    {
        left = -1,
        right = 1
    }

    class EnemyEntity
    {
        Texture2D t2d_SpritePic;
        //Only using a picture for now. Don't you worry about Animations, let ME worry about blank.

        TileCollision TTECollision;
        //TTE = Tile To Enemy.

        EnemyType enemyType;
        //which type of enemy to be loaded.

        EnemyState currentState;
        //Current enemy state.

        bool isPlayerInSight;
        //Can enemy see player?

        bool previouslyAttacked;
        //Has attacked before?

        bool AttackState;
        //Is attacking

        private SoundEffect se_PlayerHit;
        private SoundEffect se_EnemyCry;

        private SoundEffectInstance sei_PlayerHit;
        private SoundEffectInstance sei_EnemyCry;
        //
        #region Debug Values

        SpriteFont DebugFont;
        bool isDebugOn = false;

        #endregion

        #region Enemy Physics
        private MoveDirection direction = MoveDirection.left;
        private MoveDirection prevDirection;

        private float movement;
        private const float MaxWaitTime = 0.5f;
        private const float MoveSpeed = 128.0f;
        private const float MaxPatrolMovement = 200.0f;

        private const float MoveAcclereration = 14000.0f;
        private const float MaxMoveSpeed = 50.0f;
        private const float GroundDragFactor = 0.58f;
        private const float AirDragFactor = 0.65f;
        private const float GravityAcceleration = 3500.0f;
        private const float MaxFallSpeed = 1000.0f;
        #endregion

        //Parameters / Vicinty Floats
        const float f_PlayerAttackRange = 25.0f;
        const float f_EnemyVisionRangeX = 100.0f;
        const float f_EnemyVisionRangeY = 50.0f;

        float Counter = 0.0f;

        public Vector2 v2_Position
        {
            get { return v2_position; }
            set { v2_position = value; }
        }
        Vector2 v2_position;

        public Vector2 v2_PrevPosition
        {
            get { return v2_prevPosition; }
            set { v2_prevPosition = value; }
        }
        Vector2 v2_prevPosition;

        private Vector2 initialPosition;

        public Vector2 v2_Velocity
        {
            get { return v2_velocity; }
            set { v2_velocity = value; }
        }
        Vector2 v2_velocity;

        public bool IsOnGround
        {
            get { return isOnGround; }
        }
        bool isOnGround;

        bool IsCollidingWith_Stairs = false;

        bool b_NotInPatrolArea;

        private float f_previousBottom;

        private Rectangle rec_localBounds;

        public Rectangle BoundingRectangle
        {
            get
            {
                int i_left = (int)Math.Round(v2_Position.X - (t2d_SpritePic.Width / 2)) + rec_localBounds.X;
                int i_top = (int)Math.Round(v2_Position.Y - (t2d_SpritePic.Height / 2)) + rec_localBounds.Y;

                return new Rectangle(i_left, i_top, rec_localBounds.Width, rec_localBounds.Height);
            }
        }

        public EnemyEntity(ContentManager content, Vector2 sPosition, EnemyType type)
        {
            //init whatever needs to be init'd here.
            LoadContent(content, type);
            Spawn(sPosition, type);
        }

        /// <summary>
        /// Spawns an enemy at a given point
        /// </summary>
        /// <param name="sPosition">put CollisionMap.GetEnemySpawnPoint(string point) here.</param>
        /// <param name="type">put the enemy type you wish to spawn here, I'm nto doing anything with it yet, so... yeah.</param>
        public void Spawn(Vector2 sPosition, EnemyType type)
        {
            v2_position.X = (sPosition.X * CollidingTile.i_TileWidth) + (t2d_SpritePic.Width / 2);
            v2_position.Y = (sPosition.Y * CollidingTile.i_TileHeight);

            initialPosition = v2_position;

            enemyType = type;
        }

        public void LoadContent(ContentManager content, EnemyType type)
        {

            currentState = EnemyState.Patrol;

            t2d_SpritePic = content.Load<Texture2D>("GameplayScreenContent/Sprites/Nightmare - Knife");

            DebugFont = content.Load<SpriteFont>("MenuContent/Arial");

            se_PlayerHit = content.Load<SoundEffect>("Audio/SoundEffects/Hit");
            se_EnemyCry = content.Load<SoundEffect>("Audio/SoundEffects/EnemyCry");

            sei_PlayerHit = se_PlayerHit.CreateInstance();
            sei_EnemyCry = se_EnemyCry.CreateInstance();

            b_NotInPatrolArea = false;
 
            //set up animations...
            //the type will have to load the enemies first before the spawn happens?

            int i_width = 45;//(int)t2d_SpritePic.Width;
            int i_left = 0;// (int)t2d_SpritePic.Width - i_width;
            int i_height = 32;// (int)t2d_SpritePic.Height;
            int i_top = 0;// (int)t2d_SpritePic.Height - i_height;

            rec_localBounds = new Rectangle(i_left, i_top, i_width, i_height);

            direction = MoveDirection.right;

        }

        public void Update(GameTime gameTime, TileMap_Colliding collisionMap, PlayerEntity player)
        {

            player.b_IsHit = false;

            //Update collision and physics.
            Patrol(player.v2_Position);

            FearLight(player);

            if (currentState == EnemyState.Aggro)
                Seek(player.v2_Position);

            if (currentState == EnemyState.Attack)
                Attack(player);

            AttackState = previouslyAttacked;
            previouslyAttacked = false;

            prevDirection = direction;
            v2_prevPosition = v2_position;

            ApplyPhysics(gameTime, collisionMap);

        }

        public void FearLight(PlayerEntity player)
        {
            if (player.b_RequestAbilityOneActivate)
            {
                currentState = EnemyState.Avoiding;

                sei_EnemyCry.Play();

                if (v2_position.X >= player.v2_Position.X)
                {
                    v2_position.X++;
                }
                else
                {
                    v2_position.X--;
                }
            }

        }

        public void ApplyPhysics(GameTime gameTime, TileMap_Colliding collisionMap)
        {
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            Vector2 previousPosition = v2_Position;

            //velocity X
            if (!IsCollidingWith_Stairs)
            {
                v2_velocity.Y = MathHelper.Clamp(v2_velocity.Y + Physics.GravityAcceleration * elapsed, -Physics.MaxFallSpeed, Physics.MaxFallSpeed);
            }

            if (IsOnGround)
                v2_velocity.X *= Physics.GroundDragFactor;
            else
                v2_velocity.X *= Physics.AirDragFactor;

            v2_velocity.X = MathHelper.Clamp(v2_velocity.X, -Physics.MaxMoveSpeed, Physics.MaxMoveSpeed);

            v2_position += v2_velocity * elapsed;
            v2_position = new Vector2((float)Math.Round(v2_Position.X), (float)Math.Round(v2_Position.Y));

            //handle collisions
            HandleCollisions(collisionMap);

            if (v2_Position.X == previousPosition.X)
                v2_velocity.X = 0;
            if (v2_position.Y == previousPosition.Y)
                v2_velocity.Y = 0;

        }

        public void Patrol(Vector2 playerPos)
        {
            if (((v2_Position.X > playerPos.X - f_EnemyVisionRangeX) && (v2_Position.X < playerPos.X + f_EnemyVisionRangeX))
               && ((v2_Position.Y > playerPos.Y - f_EnemyVisionRangeY) && (v2_Position.Y < playerPos.Y + f_EnemyVisionRangeY)))
            {
                //If the enemy sees Mallory in his sight range:
                isPlayerInSight = true;
                currentState = EnemyState.Aggro;
            
            }
            else //Not near the player.
            {                
                isPlayerInSight = false;
                currentState = EnemyState.Patrol; // Still patrolling.

                #region Patrol Logic
                if (b_NotInPatrolArea)
                {
                    //If enemy isn't aggro'd, then get the fudgesticals back to patrolling!
                    if (v2_position.X <= (initialPosition.X - MaxPatrolMovement))
                    {
                        b_NotInPatrolArea = true;
                        v2_position.X += 3.0f;
                        //movement += 0.1f;
                    }
                    else if (v2_position.X >= (initialPosition.X + MaxPatrolMovement))
                    {
                        b_NotInPatrolArea = true;
                        v2_position.X -= 3.0f;
                        //movement -= 0.1f;
                    }
                    else
                    {
                        b_NotInPatrolArea = false;
                    }
                }
                else
                {
                    if (direction == MoveDirection.left)
                    {
                        v2_position.X -= 1.5f;
                        Counter += 1.0f;

                        if (Counter >= 100.0f)
                        {
                            direction = MoveDirection.right;
                            Counter = 0.0f;
                        }
                    }
                    else if (direction == MoveDirection.right)
                    {
                        v2_position.X += 1.5f;
                        Counter += 1.0f;

                        if (Counter >= 100.0f)
                        {
                            direction = MoveDirection.left;
                            Counter = 0.0f;
                        }

                    }
                }
                #endregion
            
            }

        }

        public void Seek(Vector2 playerPos)
        {
            //Currently seeking. Means that the enemy has been aggrivated.
            //Follow player, and get close enough to attack.

            if (v2_Position.X > playerPos.X)
            {
                direction = MoveDirection.left;
                v2_position.X -= 2.0f;
            }
            else if (v2_Position.X < playerPos.X)
            {
                direction = MoveDirection.right;
                v2_position.X += 2.0f;
            }

            if (((v2_Position.X > playerPos.X - f_PlayerAttackRange) && (v2_Position.X < playerPos.X + f_PlayerAttackRange)) &&
                ((v2_Position.Y > playerPos.Y - f_PlayerAttackRange) && (v2_Position.Y < playerPos.Y + f_PlayerAttackRange)))
            {
                currentState = EnemyState.Attack;
            }
        }

        public void Attack(PlayerEntity player)
        {
            if (!AttackState && !previouslyAttacked) //If you didn't attack before, do so now. Creates a pause in the barrage.
            {
                if (((v2_Position.X > player.v2_Position.X - f_PlayerAttackRange) && (v2_Position.X < player.v2_Position.X + f_PlayerAttackRange)) &&
                    ((v2_Position.Y > player.v2_Position.Y - f_PlayerAttackRange) && (v2_Position.Y < player.v2_Position.Y + f_PlayerAttackRange)))
                {
                    //EnemyAnimation.Animation = "Attack";
                    sei_PlayerHit.Play();
                    currentState = EnemyState.Attack;
                    player.f_HealthState -= 1.0f;
                    player.b_IsHit = true;
                    previouslyAttacked = true;
                }
            }
        }

        private void HandleCollisions(TileMap_Colliding collisionMap)
        {
            IsCollidingWith_Stairs = false;

            //Get the player's bounding rectangle and find the neighboring tiles.
            Rectangle rec_Bounds = BoundingRectangle;

            int i_leftTile = (int)Math.Floor((float)rec_Bounds.Left / CollidingTile.i_TileWidth);
            int i_rightTile = (int)Math.Ceiling(((float)rec_Bounds.Right / CollidingTile.i_TileWidth)) - 1;
            int i_topTile = (int)Math.Floor((float)rec_Bounds.Top / CollidingTile.i_TileHeight);
            int i_bottomTile = (int)Math.Ceiling(((float)rec_Bounds.Bottom / CollidingTile.i_TileHeight)) - 1;

            //Reset Flag to search for ground collision.
            isOnGround = false;

            //For Each Potentially Colliding Tile...
            for (int y = i_topTile; y <= i_bottomTile; ++y)
            {
                for (int x = i_leftTile; x <= i_rightTile; ++x)
                {
                    TTECollision = collisionMap.GetCollision(x, y);

                    #region If the Tile is a collision Tile...

                    //if it's collidable
                    if (TTECollision != TileCollision.Passable && TTECollision != TileCollision.SpawnPoint)//all collision type checks will happen here.
                    {
                        Rectangle rec_tileBounds = collisionMap.GetBounds(x, y);

                        Vector2 v2_depth = RectangleExtensions.GetIntersectionDepth(rec_Bounds, rec_tileBounds);

                        if (v2_depth != Vector2.Zero)//if actually colliding
                        {
                            //get the absolute value of the depth of intersection
                            float f_absDepthX = Math.Abs(v2_depth.X);
                            float f_absDepthY = Math.Abs(v2_depth.Y);

                            //resolve along the shallow axis.
                            if (f_absDepthY < f_absDepthX || TTECollision == TileCollision.Platform )
                            {
                                //if we crossed the top of a platform / tile, we are on the ground.
                                if (f_previousBottom <= rec_tileBounds.Top)
                                    isOnGround = true;

                                //Ignore platforms, unless we're on the ground.
                                if ((TTECollision == TileCollision.Impassable || IsOnGround))
                                {
                                    //resolve the collision along the Y axis.
                                    v2_Position = new Vector2(v2_Position.X, v2_Position.Y + v2_depth.Y);

                                    //perform further collisions with the new bounds.
                                    rec_Bounds = BoundingRectangle;
                                }

                            }

                            else if (TTECollision == TileCollision.Impassable)//Ignore platforms completely
                            {
                                //resolve collision along the X axis.
                                v2_Position = new Vector2(v2_Position.X + v2_depth.X, v2_Position.Y);

                                //perform further collisions with the new bounds
                                rec_Bounds = BoundingRectangle;
                            }

                        }
                    }

                    #endregion

                    #region OTher Tile Collision Types

                    else if (TTECollision == TileCollision.Passable)
                    {
                        isOnGround = false;
                    }

                    else if (TTECollision == TileCollision.DiagonalLeftStairs)
                    {
                        IsCollidingWith_Stairs = true;
                        if (direction == MoveDirection.left)
                        {
                            v2_position.Y -= 3.0f;
                        }
                        else if (direction == MoveDirection.right)
                        {
                            v2_position.Y += 3.0f;
                        }
                    }

                    else if (TTECollision == TileCollision.DiagonalRightStairs)
                    {
                        IsCollidingWith_Stairs = true;
                        if (direction == MoveDirection.left)
                        {
                            v2_position.Y += 3.0f;
                        }
                        else if (direction == MoveDirection.right)
                        {
                            v2_position.Y -= 3.0f;
                        }
                    }

                    else if (TTECollision == TileCollision.Ladder)
                    {
                        isOnGround = true;
                    }
                    #endregion
                }
            }

            //set velocity to zero if on the ground.
            if (IsOnGround)
                v2_velocity.Y = 0.0f;

            //save the new bounds bottom
            f_previousBottom = rec_Bounds.Bottom;

        }

        public void Draw(SpriteBatch spriteBatch)
        {
            if (direction == MoveDirection.left)
            {
                if (currentState == EnemyState.Aggro)
                {
                    spriteBatch.Draw(t2d_SpritePic, v2_Position, Color.Purple);
                }
                else if (currentState == EnemyState.Attack)
                {
                    spriteBatch.Draw(t2d_SpritePic, v2_position, Color.Red);
                }
                else if (currentState == EnemyState.Idle)
                {
                    spriteBatch.Draw(t2d_SpritePic, v2_Position, Color.White);
                }
                else if (currentState == EnemyState.Patrol)
                {
                    spriteBatch.Draw(t2d_SpritePic, v2_Position, Color.Blue);
                }
                else
                {
                    spriteBatch.Draw(t2d_SpritePic, v2_Position, Color.Black);
                }
            }
            else
            {
                if (currentState == EnemyState.Aggro)
                {
                    spriteBatch.Draw(t2d_SpritePic, v2_Position, null, Color.Purple, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.FlipHorizontally, 0.0f);
                }
                else if (currentState == EnemyState.Attack)
                {
                    spriteBatch.Draw(t2d_SpritePic, v2_position, null, Color.Red, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.FlipHorizontally, 0.0f);
                }
                else if (currentState == EnemyState.Idle)
                {
                    spriteBatch.Draw(t2d_SpritePic, v2_Position, null, Color.White, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.FlipHorizontally, 0.0f);
                }
                else if (currentState == EnemyState.Patrol)
                {
                    spriteBatch.Draw(t2d_SpritePic, v2_Position, null, Color.Blue, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.FlipHorizontally, 0.0f);
                }
                else
                {
                    spriteBatch.Draw(t2d_SpritePic, v2_Position, null, Color.Black, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.FlipHorizontally, 0.0f);
                }
            }
        }
    }
}
